my $file = $ARGV[0];


open INPUT, "<$file" or die "cannot open $file";

$MINNODE=0;
$MAXNODE=1300;
$MIN_SEQ = 0;
$MAX_SEQ = 65535;
$MAX_TIME = hex("ffffffff");
$NORM_MIN_SEQ = 0; #nomorlized seq;
$NORM_MAX_SEQ = $MAX_SEQ - $MIN_SEQ;
#$AHEAD_BUFFER_SIZE = 5;
$CYCLE_LENGTH = 30*60*1024; #cycle length 30 min;
$MAX_ERROR = 15; #maximum error;


@allids = ();
@allseqs = ();
@alltypes = ();
@alltimes = ();
@allpctimes = ();

#read the file
$totalPacketNum = 0;
print "reading input file...\n";
while($line = <INPUT>)
{
	chomp;
	@rs = split(' ', $line);
	#print "$currentTime $start $end\n";	
	$alltypes[$totalPacketNum] = hex("$rs[19]");
	if ($alltypes[$totalPacketNum] < hex("C1") || $alltypes[$totalPacketNum] > hex("C4"))
	{
		next;
	}

	$allids[$totalPacketNum] = hex("$rs[20]$rs[21]");#my am addr
	
	# if ($allids[$totalPacketNum] != 1112)
	# {
		# next;
	# }
	
	$allseqs[$totalPacketNum] = hex("$rs[32]$rs[33]");#sequence number
  #$source_time = hex("$rs[24]$rs[25]$rs[26]$rs[27]");
	$alltimes[$totalPacketNum] = hex("$rs[24]$rs[25]$rs[26]$rs[27]");
	$allpctimes[$totalPacketNum] = $rs[0];
	$totalPacketNum++;
}
close INPUT;

#sort the data
print "sorting file ...\n";
@ranks = sort {$allids[$a]<=>$allids[$b] 
			or $alltypes[$a] <=> $alltypes[$b]
			} 0..$#allids;
			
@allids = @allids[@ranks];
@allseqs = @allseqs[@ranks];
@alltypes = @alltypes[@ranks];
@alltimes = @alltimes[@ranks];
@allpctimes = @allpctimes[@ranks];

print "saving sorted results ...\n";
open OUTPUT, ">./output/allpackets.lost" or die "cannot open ";
for ($tempIndex = 0; $tempIndex < @allids; ++$tempIndex) 
{
		print OUTPUT "$allids[$tempIndex]\t$alltypes[$tempIndex]\t$allseqs[$tempIndex]\t$alltimes[$tempIndex]\t$allpctimes[$tempIndex]\n"
}
close OUTPUT;

#process the data

foreach $tempseq (@allseqs)#nomorlize the seq
{
	$tempseq = $tempseq - $MIN_SEQ;
}


open OUTPUT, ">./output/AllLostPackets.dat";
open LOSTRATIOFILE, ">./output/LostRatios.dat";
$packetIndex = 0;

while($packetIndex < $totalPacketNum ){
	$tempid = $allids[$packetIndex];
	$tempseq = $allseqs[$packetIndex];
	$temptype = $alltypes[$packetIndex];
	$sameIdTypeStart = $packetIndex;
	#|| ($tempid != 1123 || $temptype != 193)
	if ($tempid < $MINNODE || $tempid > $MAXNODE){ #do not process incorrect ids.
		++$packetIndex;
		next;
	}
	while ($packetIndex < $totalPacketNum 
					&& $allids[$packetIndex] == $tempid	&& $alltypes[$packetIndex] == $temptype)
	{
		++$packetIndex;
	}
	
	@seq2packet = (); #record whether the packet is received.use nomorlized seq;
	@seq2index = ();	
	@lostPacketSeqs = (); #record the seqs of lost packets
	@lostPacketUpperTime = ();
	@lostPacketLowerTime = ();
	$totalLost = 0;
	$totalReceived = 0;
	print "processing id=$tempid type=$temptype start:$sameIdTypeStart end:$packetIndex \n";
	&ProcessPackets($sameIdTypeStart, $packetIndex);
	
	$Prr = 0;
	if ($totalReceived != 0){
		$Prr = $totalReceived/($totalLost + $totalReceived);
	}
	print LOSTRATIOFILE "$tempid\t$temptype\t$totalReceived\t$totalLost\t$Prr\n";
	
	$temptype = sprintf("%X", $temptype);
	for ($tempi = 0; $tempi < @lostPacketSeqs; ++$tempi){
		print OUTPUT "$tempid\t$temptype\t$lostPacketSeqs[$tempi]\t$lostPacketLowerTime[$tempi]\t$lostPacketUpperTime[$tempi]\n";
	}
}
close OUTPUT;
close LOSTRATIOFILE;


sub SeqValid
{
	my ($startTime, $endTime, $startSeq, $endSeq) = (shift, shift, shift, shift);
	return 1;
}

sub TimePeriodValid
{
	my ($startTime, $endTime) = (shift, shift);
	return 1;
}
#process the lost packets for the same <source, type>
sub ProcessPackets{
	my ($tempstart, $tempend) = (shift, shift);
	#print "$tempstart $tempend \n";
	my $tempi = $tempstart;
	my $lastSeq = -1, $firstSeq = -1; # record the first packet and last packet
	my $timediff = 0, $seqdiff = 0;
	my $maxError = 0;
	my $isRestart = 0, $isTimeOverflow = 0, $isSeqOverflow = 0, $bothOverflow = 0, $seqdiff2 = 0, $timediff2 = 0;
	my $isRand = 0;
	while ($tempi < $tempend){
		if ($allseqs[$tempi] < $NORM_MIN_SEQ || $allseqs[$tempi] > $NORM_MAX_SEQ){
			$tempi += 1;
			next;
		}
		if ($lastSeq == -1){
			$lastSeq = $allseqs[$tempi];
			$firstSeq = $lastSeq;
			$seq2packet[$lastSeq] = 1;
			$seq2index[$lastSeq] = $tempi;
			$totalReceived += 1;
		}else{
#			$isRand = 0;
#			if (rand(100) > 95){
#				$allseqs[$tempi] = rand($MAX_SEQ);
#				print "rand: $allseqs[$tempi]\n";
#				$isRand = 1;
#			}
			$tempseq = $allseqs[$tempi];
			$seqdiff = $allseqs[$tempi] - $allseqs[$seq2index[$lastSeq]];
			$timediff = $alltimes[$tempi] - $alltimes[$seq2index[$lastSeq]];
			
			$timediff = $timediff - $seqdiff * $CYCLE_LENGTH;
			$maxError = $MAX_ERROR;
			#we need to deal with four cases, node reboot, time overflow, seq overflow, both time and seq overflow
			#first check seq overflow
			$seqdiff2 = $MAX_SEQ - $allseqs[$seq2index[$lastSeq]]  + $allseqs[$tempi] + 1;
			$timediff2 = $alltimes[$tempi] - $alltimes[$seq2index[$lastSeq]];
			$timediff2 = $timediff2 - $seqdiff2 * $CYCLE_LENGTH;
			$isSeqOverflow = 0;
			$isTimeOverflow = 0;
			$isRestart = 0;
			$bothOverflow = 0;
			if (abs($timediff2 ) < $MAX_ERROR) 
			{
				#print $timediff2, " seq overflow\n";
				$isSeqOverflow = 1;
			}
			$timediff2 = $alltimes[$tempi] + $MAX_TIME - $alltimes[$seq2index[$lastSeq]];
			if (abs($timediff2 - $seqdiff * $CYCLE_LENGTH) < $MAX_ERROR) 
			{
				$isTimeOverflow = 1;
				#print $timediff2 - $seqdiff * $CYCLE_LENGTH, "Timeoverflow\n";
			}
			if (abs($timediff2 - $seqdiff2 * $CYCLE_LENGTH) < $MAX_ERROR) 
			{
				$bothOverflow = 1;
				#print "bothOverflow ", $timediff2 - $seqdiff2 * $CYCLE_LENGTH, "\n";
			}
			if (abs($timediff) < $CYCLE_LENGTH) 
			{
				$isRestart = 1;
				#print $timediff, "restart\n";
			}
			#print "lastSeq:$lastSeq timediff:$timediff seqdiff:$seqdiff maxError:$maxError diff:".($timediff - $seqdiff * $CYCLE_LENGTH)."\n";
			if (abs($timediff) < $MAX_ERROR && ($seqdiff) > -10){ 			#seq normal.
				#print "timediff: ". $timediff . " seftimediff:" . $seqdiff * $CYCLE_LENGTH . "\n";
				$seq2packet[$tempseq] = 1;
				$seq2index[$tempseq] = $tempi;
				$totalReceived += 1;
				if ($tempseq > $lastSeq){
					$lastSeq = $tempseq;
				}else {
					print "find an out-of-order packet index $tempi (lastseq:$lastSeq, cur: $tempseq, $allids[$tempi] $alltypes[$tempi]) timediff:$timediff\n";
				}
				if ($tempseq < $firstSeq){
					$firstSeq = $tempseq;
				}
#				if ($isRand){
#					print "rand treated as correct $tempseq, $lastSeq $timediff $seqdiff\n";
#				}
			}elsif ( $isRestart || $isSeqOverflow || $isTimeOverflow || $bothOverflow){ #assume the ndoe rebooted
				#check node restart
				$isRestart = 0;
				$isSeqOverflow = 0;
				$isTimeOverflow = 0;
				$bothOverflow = 0;
				#$timediff = $alltimes[$tempi] - $allseqs[$tempi]*$CYCLE_LENGTH;
				print "node restart--cur seq: ". $tempseq . " timediff: $timediff " . "seqdiff:$seqdiff " . " lastSeq: $lastSeq " . "\n";
				last; #node restart. start a new sequence.
			}else{
				print "seq error $allseqs[$tempi] $lastSeq \n";
			}
			# we suppose error are large than $CYCLE_LENGTH, meanwhile, errors cannot be grouped. Thus error will have limited effect.
		}
		$tempi += 1;
	}
	my $tempUpperTime = -1, $tempLowerTime = -1, $mytempindex = 0, $tempHasPacketLostInterval = 0;
	for ($tempcheck = $firstSeq; $tempcheck <= $lastSeq; ++$tempcheck){
		if ($seq2packet[$tempcheck] != 1){
			$totalLost++;
			push(@lostPacketSeqs, $tempcheck);
			$tempHasPacketLostInterval++;
			#print "$tempid  $temptype  $tempcheck $tempi\n";
		}else {
			$mytempindex = $seq2index[$tempcheck];
			if ($tempHasPacketLostInterval){
				$tempUpperTime = $allpctimes[$mytempindex];
				for ($temp = 0; $temp < $tempHasPacketLostInterval; ++$temp){
					push(@lostPacketLowerTime, $tempLowerTime); # use recorded time as lower time
					push(@lostPacketUpperTime, $tempUpperTime);# use current time as upper time
				}
				$tempLowerTime = $tempUpperTime;
				$tempHasPacketLostInterval = 0;
			}else {
				$tempLowerTime = $allpctimes[$mytempindex];
			}
		}	
	}

	if ($tempi < $tempend){
		@seq2packet = ();
		@seq2index = ();
		&ProcessPackets($tempi, $tempend);
	}
}



#			if ($tempseq >= $lastSeq)
#			{
#				$timediff = $alltimes[$tempi] - $alltimes[$seq2index[$lastSeq]];
#				$seqdiff = $allseqs[$tempi] - $allseqs[$seq2index[$lastSeq]];
#			}else 
#			{
#				my $tempj = $tempi - 1;
#				while ($tempj >= $tempstart)
#				{
#					if ($seq2packet[$tempj] == 1)
#					{
#						last;
#					}
#				}
#				
#				if ($tempj < 0)
#				{
#					$tempj = $tempi + 1;
#					while ($tempj <= $lastSeq)
#					{
#						if ($seq2packet[$tempj] == 1)
#						{
#							last;
#						}
#					}
#				}


	#check lost packets
#	my $currentIndex = $sameIdTypeStart, $currentIndexEnd = $packetIndex;
#	my $bufferEnd = -1;
#	while ($currentIndex < $currentIndexEnd - 1){# only consider until the second last packet
#		$bufferEnd = $currentIndex + $AHEAD_BUFFER_SIZE;
#		if ($bufferEnd > $currentIndexEnd){
#			$bufferEnd = $currentIndexEnd;
#		}
#		if (&TimePeriodValid() != 1){
#			next;
#		}
#		if (
#		if ($allseqs[$currentIndex + 1] - $allseqs[$currentIndex] == 1) {
#			
#		}
#	}